#!/bin/bash

set -e

# 设置confd

if [ "$USE_CONFD" = "true" ]; then
  if [ -z "$CONFD_BACKEND" ] && curl -sf http://rancher-metadata.rancher.internal/latest > /dev/null; then
    export CONFD_BACKEND=rancher
  fi
  if [ "$CONFD_BACKEND" = "rancher" ]; then
    if [ -z "$RANCHER_API_VERSION" ]; then
      export RANCHER_API_VERSION=2016-07-29
    fi
    confd $CONFD_OPTS -backend rancher -prefix /$RANCHER_API_VERSION -node rancher-metadata.rancher.internal -onetime
  else
    if [ -z "$CONFD_BACKEND" ]; then
      export CONFD_BACKEND=env
    fi
    confd $CONFD_OPTS -backend $CONFD_BACKEND -onetime
  fi
fi

# 设置java参数

size_in_bytes() {
  local S=$1
  if [[ "$S" == *k ]] || [[ "$S" == *K ]]; then
    S=${S:0:${#S}-1}
    S=$(expr $S \* 1024)
  elif [[ "$S" == *m ]] || [[ "$S" == *M ]]; then
    S=${S:0:${#S}-1}
    S=$(expr $S \* 1024 \* 1024)
  elif [[ "$S" == *g ]] || [[ "$S" == *G ]]; then
    S=${S:0:${#S}-1}
    S=$(expr $S \* 1024 \* 1024 \* 1024)
  fi
  echo $S
}

get_memory_limit_from_cgroup() {
  local ML=""
  if [ -f /sys/fs/cgroup/memory/memory.limit_in_bytes ]; then
    ML=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
    if [ ${#ML} -gt 12 ]; then
      # unlimited in docker container
      ML=""
    fi
  fi
  echo $ML
}

get_memory_limit_from_linux() {
  local ML=""
  if [ -f /proc/meminfo ]; then
    ML=$(cat /proc/meminfo | grep MemAvailable | awk '{print $2}')
    if [ -z "$ML" ]; then
      ML=$(cat /proc/meminfo | grep MemTotal | awk '{print $2}')
    fi
    if [ ! -z "$ML" ]; then
      ML=$(expr $ML \* 1024)
    fi
  fi
  echo $ML
}

get_memory_limit() {
  local ML=$(get_memory_limit_from_cgroup)
  if [ -z "$ML" ]; then
    ML=$(get_memory_limit_from_linux)
  fi
  echo $ML
}

to_mb() {
  local S=$1
  if [ ! -z "$S" ]; then
    S=$(expr $S / 1024 / 1024)
    if [ $S -lt 1 ]; then
      S="0"
    else
      S="${S}m"
    fi
  fi
  echo $S
}

# 设置内存上限

if [ "$JAVA_DISABLE_CGROUP" = "true" ]; then

  if [ -z "$MEM_LIMIT" ]; then
    export MEM_LIMIT=$(get_memory_limit)
  fi

  if [ -z "$MEM_LIMIT" ]; then
    echo "Cannot find out max memory size"
    exit 1
  fi

  # 计算app可以使用的内存
  if [ -z "$MEM_RESERVE" ]; then
    MEM_RESERVE=100m
  fi
  export MEM_RESERVE=$(size_in_bytes $MEM_RESERVE)
  if [ -z "$JAVA_MEM_INTERNAL" ]; then
    JAVA_MEM_INTERNAL=500m
  fi
  export JAVA_MEM_INTERNAL=$(size_in_bytes $JAVA_MEM_INTERNAL)
  APP_MEM=$(expr $MEM_LIMIT - $MEM_RESERVE - $JAVA_MEM_INTERNAL)
  # 根据JAVA_MEM_OFF_HEAP_PERCENT计算heap size
  if [ ! -z "$JAVA_MEM_OFF_HEAP_PERCENT" ]; then
    if [[ "$JAVA_MEM_OFF_HEAP_PERCENT" == *'%' ]]; then
      export JAVA_MEM_OFF_HEAP_PERCENT=${JAVA_MEM_OFF_HEAP_PERCENT:0:-1}
    fi
    export JAVA_MEM_OFF_HEAP=$(expr $APP_MEM \* $JAVA_MEM_OFF_HEAP_PERCENT / 100)
  fi
  # 根据Off heap size计算heap size
  if [ -z "$JAVA_MEM_OFF_HEAP" ]; then
    export JAVA_MEM_OFF_HEAP=134217728 # 128M
  fi
  export JAVA_MEM_HEAP=$(expr $APP_MEM - $JAVA_MEM_OFF_HEAP)
  # 总是设置off heap size，避免被OOM-killer杀死
  JVM_OPTS="$JVM_OPTS -XX:MaxDirectMemorySize=$(to_mb $JAVA_MEM_OFF_HEAP)"
  # 设置heap size上限
  JVM_OPTS="$JVM_OPTS -Xmx$(to_mb $JAVA_MEM_HEAP)"
  # 设置heap size下限
  if [ -z "$JAVA_MEM_PREALLOC" ]; then
    export JAVA_MEM_PREALLOC=true
  fi
  if [ "$JAVA_MEM_PREALLOC" = "true" ]; then
    JVM_OPTS="$JVM_OPTS -Xms$(to_mb $JAVA_MEM_HEAP)"
  fi
else
  JVM_OPTS="$JVM_OPTS -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
fi

# 打开远程调试端口

if [ ! -z "$JAVA_DEBUG_PORT" ]; then
  JVM_OPTS="$JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,address=$JAVA_DEBUG_PORT,suspend=n"
fi

# 打开JMX端口

if [ ! -z "$JAVA_JMX_PORT" ]; then
  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.port=$JAVA_JMX_PORT"
  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.rmi.port=$JAVA_JMX_PORT"
  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.local.only=false"
  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
  JVM_OPTS="$JVM_OPTS -Dcom.sun.management.jmxremote.ssl=false"
  if [ -z "$JAVA_RMI_HOST" ]; then
    export JAVA_RMI_HOST=_rancher_
  fi
fi

if [ ! -z "$JAVA_RMI_HOST" ]; then
  HOST="$JAVA_RMI_HOST"
  if [ -z "$HOST" ] || [ "$HOST" = "_rancher_" ]; then
    # 尝试从rancher中获取域名
    HOST=$(curl -s http://rancher-metadata/latest/self/container/name)
    if [ $? -eq 0 ]; then
      HOST="$HOST.rancher.internal"
    else
      HOST=""
    fi
  fi
  if [ -z "$HOST" ] || [ "$HOST" = "_ip_" ]; then
    # 获取ip
    HOST=$(ip route | awk '/src/ { print $5 }')
  fi
  JVM_OPTS="$JVM_OPTS -Djava.rmi.server.hostname=$HOST"
fi

# 设置时区，java时区不遵循操作系统配置，需要单独设置

if [ -z "$JAVA_TIMEZONE" ]; then
  export JAVA_TIMEZONE="Asia/Shanghai"
fi
JVM_OPTS="$JVM_OPTS -Duser.timezone=$JAVA_TIMEZONE"

# 设置临时目录，默认不使用/tmp，因为据说可能有cron任务（或应用）清空tmp目录，会导致故障

if [ -z "$JAVA_TEMPDIR" ]; then
  export JAVA_TEMPDIR=/javatmp
fi

mkdir -p $JAVA_TEMPDIR
JVM_OPTS="$JVM_OPTS -Djava.io.tmpdir=$JAVA_TEMPDIR"

# 设置dump和crash日志目录

if [ -z "$JAVA_LOGDIR" ]; then
  export JAVA_LOGDIR=$JAVA_TEMPDIR
fi

if [ ! -d "$JAVA_LOGDIR" ]; then
  mkdir -p $JAVA_LOGDIR
fi

DATE=$(date +%Y%m%d%H%M%S)
if [ "$JAVA_DISABLE_HEAP_DUMP" = "true" ]; then
  JVM_OPTS="$JVM_OPTS -XX:ErrorFile=$JAVA_LOGDIR/hs_err_$DATE.log"
elif [ -z "$HOSTNAME" ]; then
  JVM_OPTS="$JVM_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$JAVA_LOGDIR/jvm-$DATE.hprof -XX:ErrorFile=$JAVA_LOGDIR/hs_err_$DATE.log"
else
  JVM_OPTS="$JVM_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$JAVA_LOGDIR/jvm-$HOTNAME-$DATE.hprof -XX:ErrorFile=$JAVA_LOGDIR/hs_err_$HOSTNAME_$DATE.log"
fi

# 打印GC

JVM_OPTS="$JVM_OPTS -XX:+PrintCommandLineFlags -XX:+PrintGC"

# 垃圾回收

if [ ! "$JAVA_G1" = "false" ]; then
  JVM_OPTS="$JVM_OPTS -XX:+UseG1GC -XX:+PrintAdaptiveSizePolicy"
fi

# 追加用户参数，用户参数放在后面可以覆盖默认参数

export JAVA_OPTS="$JVM_OPTS $JAVA_OPTS"

# 启动应用

exec $@

# 支持以下环境变量：

# MEM_LIMIT 系统内存大小，可以用k/m/g结尾。若不设置此参数，则根据cgroup或者系统信息自动计算。
# MEM_RESERVE 留给系统的内存大小，可以用k/m/g结尾。默认100M。
# JAVA_OPTS jvm启动参数。
# JAVA_DISABLE_CGROUP 若设置为"true"，则不使用cgroup计算内存，而是用脚本计算内存。默认为false。
# JAVA_MEM_PREALLOC 若设置此变量为"true"，则jvm预占用堆内存，即-Xms和-Xmx参数值相等。默认为"true"。仅当JAVA_DISABLE_CGROUP=false时有效。
# JAVA_MEM_OFF_HEAP jvm off heap内存大小，默认为128M。仅当JAVA_DISABLE_CGROUP=false时有效。
# JAVA_MEM_OFF_HEAP_PERCENT jvm off head内存百分比，可以写做"50%"或者"50"，这个参数和JAVA_MEM_OFF_HEAP互斥。仅当JAVA_DISABLE_CGROUP=false时有效。
# JAVA_MEM_INTERNAL jvm内部占用内存大小。默认500M。这个参数无法限制jvm内存占用，只用于计算过程中将jvm内存预留出来。仅当JAVA_DISABLE_CGROUP=false时有效。
# JAVA_DEBUG_PORT jvm远程调试端口号。若设置此参数，则开启远程调试。
# JAVA_JMX_PORT JMX端口号。默认不开启JMX，若设置此参数，则开启JMX。
# JAVA_TIMEZONE 指定时区，默认为Asia/Shanghai
# JAVA_TEMPDIR 临时目录。默认值/javatmp。
# JAVA_LOGDIR jvm dump和crash日志输出目录，若不设置此参数，则使用JAVA_TEMPDIR目录输出日志。
# JAVA_G1 使用G1垃圾回收器，默认为true。
